package com.huicun.pay.wxpay;

import cn.hutool.json.XML;
import com.huicun.pay.utils.HttpUtils;
import com.huicun.pay.utils.SecureUtil;
import com.huicun.pay.wxpay.paramevo.CreateOrderParames;
import com.huicun.pay.wxpay.resultvo.UnifiedOrderV1Vo;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * @Time: 2021/4/12 19:28
 * @Author: lidiwei
 */
@Slf4j
public class WxPayService {
    private final static String unifiedOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    private final static String orderQueryUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    private WxPayConfig wxPayConfig;
    private ExceptionHandler exceptionHandler;

    public WxPayService(WxPayConfig wxPayConfig) {
        this.wxPayConfig = wxPayConfig;
    }

//###########################################  小程序下单  ###################################################################


    /**
     * 微信小程序下单
     *
     */
    public HashMap<String, String>  createAppletsOrder(
            String attach,
            String openid,
            String body,
            String nonceStr,
            String outTradeNo,
            String spbillCreateIp,
            String totalFee,
            String notifyUrl

    ) throws Exception {
       return createAppletsOrder(new CreateOrderParames(
                attach,
                openid,
                body,
                nonceStr,
                outTradeNo,
                spbillCreateIp,
                totalFee,
                notifyUrl
        ));
    }

    /**
     * 微信小程序下单
     */
    public  HashMap<String, String> createAppletsOrder(CreateOrderParames createOrderParames) throws Exception {
        //统一下单
        UnifiedOrderV1Vo unifiedOrderV1Vo = unifiedOrderV1(createOrderParames);
        //小程序二次签名
        return applets2Sign(unifiedOrderV1Vo);
    }

    /**
     * 小程序二次签名
     */
    private HashMap<String, String> applets2Sign(UnifiedOrderV1Vo unifiedOrderV1Vo) {
        try {
            HashMap<String, String> map = new HashMap<>();
            map.put("appId", unifiedOrderV1Vo.getAppid());
            map.put("timeStamp", System.currentTimeMillis() + "");
            map.put("nonceStr", unifiedOrderV1Vo.getNonce_str());
            map.put("package", "prepay_id=" + unifiedOrderV1Vo.getPrepay_id());
            map.put("signType", "MD5");
            map.put("paySign", signCs(map));
            return map;
        } catch (Exception e) {
            throw new RuntimeException("微信小程序下单失败");
        }
    }

//###########################################  H5下单  ###################################################################
    /**
     * 微信小程序下单
     */
    public  HashMap<String, String> createH5Order(CreateOrderParames createOrderParames) throws Exception {
        //统一下单
        UnifiedOrderV1Vo unifiedOrderV1Vo = unifiedOrderV1(createOrderParames);
        //H5二次签名
        return h52Sign(unifiedOrderV1Vo);
    }

    private HashMap<String, String> h52Sign(UnifiedOrderV1Vo unifiedOrderV1Vo) {
        try {
            HashMap<String, String> map = new HashMap<>();
            //微信就是小写
            String timeStamp = System.currentTimeMillis() + "";
            map.put("timeStamp", timeStamp);
            map.put("nonceStr", unifiedOrderV1Vo.getNonce_str());
            map.put("package", "prepay_id=" + unifiedOrderV1Vo.getPrepay_id());
            map.put("signType", "MD5");
            map.put("appId", wxPayConfig.getH5AppId());
            map.put("paySign", signCs(map));
            map.put("timestamp", timeStamp);
            return map;
        } catch (Exception e) {
            throw new RuntimeException("微信H5下单失败");
        }
    }

//###########################################  统一下单  ###################################################################

    /**
     * 统一下单V1
     *
     * <xml>
     * <return_code><![CDATA[SUCCESS]]></return_code>
     * <return_msg><![CDATA[OK]]></return_msg>
     * <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
     * <mch_id><![CDATA[10000100]]></mch_id>
     * <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
     * <openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid>
     * <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
     * <result_code><![CDATA[SUCCESS]]></result_code>
     * <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
     * <trade_type><![CDATA[JSAPI]]></trade_type>
     * </xml>
     */
    private UnifiedOrderV1Vo unifiedOrderV1(
            CreateOrderParames createOrderParames
    ) throws Exception {
        String xmlParame = buildXmlParame(createOrderParames);
        String xmlResultStr = HttpUtils.postXml(unifiedOrderUrl, xmlParame);
        log.info("统一下单返回:{}", xmlResultStr);
        try {
            UnifiedOrderV1Vo unifiedOrderV1Vo = XML.toJSONObject(xmlResultStr).getJSONObject("xml").toBean(UnifiedOrderV1Vo.class);
            if (unifiedOrderV1Vo.isSuccess()) {
                return unifiedOrderV1Vo;
            }
        } catch (Exception ignored) {

        }
        log.error("统一下单失败:{}", xmlResultStr);
        throw new RuntimeException("统一下单失败");

    }

    private String buildXmlParame(
            CreateOrderParames createOrderVO
    ) {

        HashMap<String, String> map = new HashMap<String, String>();
        map.put("appid", wxPayConfig.getAppLetsAppId());
        map.put("mch_id", wxPayConfig.getMchId());

        if (createOrderVO.getOpenid() != null) {
            map.put("openid", createOrderVO.getOpenid());
        }
        map.put("attach", createOrderVO.getAttach());
        map.put("body", createOrderVO.getBody());
        map.put("nonce_str", createOrderVO.getNonceStr());
        map.put("out_trade_no", createOrderVO.getOut_trade_no());
        map.put("spbill_create_ip", createOrderVO.getSpbill_create_ip());
        map.put("total_fee", createOrderVO.getTotal_fee());
        map.put("notify_url", createOrderVO.getNotify_url());
        map.put("trade_type", "JSAPI");
        String sign = signCs(map);
        map.put("sign", sign);
        String openidStr = "";
        if (createOrderVO.getOpenid() != null) {
            openidStr = "<openid>" + createOrderVO.getOpenid() + "</openid>";
        }
        return "<xml>" +
                "     <appid>" + map.get("appid") + "</appid>" +
                openidStr +
                "     <attach>" + map.get("attach") + "</attach>" +
                "     <body>" + map.get("body") + "</body>" +
                "     <mch_id>" + map.get("mch_id") + "</mch_id>" +
                "     <nonce_str>" + map.get("nonce_str") + "</nonce_str>" +
//                "     <notify_url>" + map.get("notify_url") + "</notify_url>" +
                "     <notify_url>" + createOrderVO.getNotify_url() + "</notify_url>" +
                "     <out_trade_no>" + map.get("out_trade_no") + "</out_trade_no>" +
                "     <spbill_create_ip>" + map.get("spbill_create_ip") + "</spbill_create_ip>" +
                "     <total_fee>" + map.get("total_fee") + "</total_fee>" +
                "     <trade_type>" + map.get("trade_type") + "</trade_type>" +
                "     <sign>" + map.get("sign") + "</sign>" +
                "     </xml>";
    }

    private String signCs(HashMap<String, String> map) {
        // 对所有传入参数按照字段名的 ASCII 码从小到大排序（字典序）
        List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(map.entrySet());
        infoIds.sort(new Comparator<Map.Entry<String, String>>() {
            @Override
            public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
                return (o1.getKey()).compareTo(o2.getKey());
            }
        });
        // 构造URL 键值对的格式
        StringBuilder stringBuilder = new StringBuilder();
        for (Map.Entry<String, String> infoId : infoIds) {
            if (infoId.getKey() != null && !"".equals(infoId.getKey())) {
                String key = infoId.getKey();
                String value = infoId.getValue();
                stringBuilder
                        .append(key)
                        .append("=")
                        .append(value).append("&");
            }
        }
//        String signTemp = stringBuilder.substring(0, stringBuilder.length() - 1);
        String signTemp = stringBuilder.toString();
        //连接商户key：
        signTemp = signTemp + "key=" + wxPayConfig.getApiKey();
        System.out.println(signTemp);
        return SecureUtil.md5(signTemp).toUpperCase();
    }

}
